package com.engine.salary.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.component.WeaTableColumnGroup;
import com.engine.salary.encrypt.EncryptUtil;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctConfig;
import com.engine.salary.entity.salaryacct.bo.SalaryAcctResultBO;
import com.engine.salary.entity.salaryacct.dto.SalaryComparisonResultListDTO;
import com.engine.salary.entity.salaryacct.param.SalaryAcctEmployeeQueryParam;
import com.engine.salary.entity.salaryacct.param.SalaryComparisonResultQueryParam;
import com.engine.salary.entity.salaryacct.po.ExcelAcctResultPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO;
import com.engine.salary.entity.salaryformula.ExpressFormula;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salarysob.bo.SalarySobItemAggregateBO;
import com.engine.salary.entity.salarysob.dto.SalarySobItemAggregateDTO;
import com.engine.salary.entity.salarysob.po.*;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.salaryacct.ExcelAcctResultMapper;
import com.engine.salary.service.*;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import weaver.hrm.User;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 薪资核算的线下对比结果
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class SalaryComparisonResultServiceImpl extends Service implements SalaryComparisonResultService {
    private EncryptUtil encryptUtil = new EncryptUtil();

    private ExcelAcctResultMapper getExcelAcctResultMapper() {
        return MapperProxyFactory.getProxy(ExcelAcctResultMapper.class);
    }

    private SalaryAcctResultService getSalaryAcctResultService(User user) {
        return ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user);
    }

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    private SalarySobItemService getSalarySobItemService(User user) {
        return ServiceUtil.getService(SalarySobItemServiceImpl.class, user);
    }

    private SalaryAcctRecordService getSalaryAcctRecordService(User user) {
        return ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user);
    }

    private SalarySobEmpFieldService getSalarySobEmpFieldService(User user) {
        return ServiceUtil.getService(SalarySobEmpFieldServiceImpl.class, user);
    }

    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }


    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SalaryFormulaService getSalaryFormulaService(User user) {
        return ServiceUtil.getService(SalaryFormulaServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

    private SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private SalarySobItemGroupService getSalarySobItemGroupService(User user) {
        return ServiceUtil.getService(SalarySobItemGroupServiceImpl.class, user);
    }

    private SalarySobItemHideService getSalarySobItemHideService(User user) {
        return ServiceUtil.getService(SalarySobItemHideServiceImpl.class, user);
    }

    private SalaryAcctSobConfigService getSalaryAcctSobConfigService(User user) {
        return ServiceUtil.getService(SalaryAcctSobConfigServiceImpl.class, user);
    }

    @Override
    public List<ExcelAcctResultPO> listBySalaryAcctRecordId(Long salaryAcctRecordId) {
        List<ExcelAcctResultPO> excelAcctResultPOS = getExcelAcctResultMapper().listSome(ExcelAcctResultPO.builder().salaryAcctRecordId(salaryAcctRecordId).build());
        return encryptUtil.decryptList(excelAcctResultPOS, ExcelAcctResultPO.class);
    }

    @Override
    public List<ExcelAcctResultPO> listBySalaryAcctEmployeeIds(Collection<Long> salaryAcctEmployeeIds) {
        if (CollectionUtils.isEmpty(salaryAcctEmployeeIds)) {
            return Collections.emptyList();
        }

        List<ExcelAcctResultPO> excelAcctResultPOS = new ArrayList<>();
        List<List<Long>> partition = Lists.partition((List<Long>) salaryAcctEmployeeIds, 2000);
        partition.forEach(empIds -> {
            excelAcctResultPOS.addAll(getExcelAcctResultMapper().listSome(ExcelAcctResultPO.builder().salaryAcctEmpIds(empIds).build()));
        });
        return encryptUtil.decryptList(excelAcctResultPOS, ExcelAcctResultPO.class);
    }

    @Override
    public SalaryComparisonResultListDTO listPageByParam(SalaryComparisonResultQueryParam queryParam) {
        return listByParam(true, queryParam);
    }

    @Override
    public SalaryComparisonResultListDTO listByParam(SalaryComparisonResultQueryParam queryParam) {
        return listByParam(false, queryParam);
    }

    /**
     * 根据薪资核算人员查询薪资核算线下对比结果
     *
     * @param queryParam 列表查询条件
     * @return
     */
    private SalaryComparisonResultListDTO listByParam(boolean needPage, SalaryComparisonResultQueryParam queryParam) {
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }

        SalaryAcctConfig salaryAcctConfig = getSalaryAcctSobConfigService(user).getSalaryAcctConfig(queryParam.getSalaryAcctRecordId());

        // 查询薪资核算所用薪资账套
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        // 查询薪资核算所用的薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = salaryAcctConfig.getSalarySobEmpFields();
        // 查询薪资账套的薪资项目分类
        List<SalarySobItemGroupPO> salarySobItemGroupPOS = salaryAcctConfig.getSalarySobItemGroups();
        // 查询薪资核算所用薪资账套的薪资项目副本
        List<SalarySobItemPO> salarySobItemPOS = salaryAcctConfig.getSalarySobItems();
        // 过滤在账套中隐藏的薪资项目
        salarySobItemPOS = salarySobItemPOS.stream().filter(po -> po.getItemHide() == null || po.getItemHide() == 0).collect(Collectors.toList());
        // 查询公式详情
        Set<Long> formulaIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getFormulaId);
        List<ExpressFormula> expressFormulas = getSalaryFormulaService(user).listExpressFormula(formulaIds);
        // 转换成
        Map<Long, String> expressFormulaMap = SalaryEntityUtil.convert2Map(expressFormulas, ExpressFormula::getId, ExpressFormula::getFormula);
        Map<Long, String> customParameters = SalaryEntityUtil.convert2Map(salarySobItemPOS, SalarySobItemPO::getSalaryItemId, salarySobItemPO -> {
            if (salarySobItemPO.getFormulaId() <= 0) {
                return SalaryI18nUtil.getI18nLabel(92004, "输入/导入");
            }
            return expressFormulaMap.getOrDefault(salarySobItemPO.getFormulaId(), StringUtils.EMPTY);
        });
        // 查询薪资项目
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(salaryItemIds);
        // 转换成聚合dto
        SalarySobItemAggregateBO salarySobItemAggregateBO = new SalarySobItemAggregateBO(salarySobPO, salarySobEmpFieldPOS,
                salarySobItemGroupPOS, salarySobItemPOS, expressFormulas, salaryItemPOS, Collections.emptyList(), Collections.emptyList());
        SalarySobItemAggregateDTO salarySobItemAggregateDTO = salarySobItemAggregateBO.convert2AggregateDTO();

        // 薪资核算人员
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByResultQueryParam(queryParam);
        if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) {
            // 构建薪资核算结果列表表头
            List<WeaTableColumnGroup> weaTableColumns = SalaryAcctResultBO.buildTableColumns4ComparisonResultByGroup(salarySobItemAggregateDTO, Collections.emptySet());
            // 构建列表数据
            // 返回结果
            return new SalaryComparisonResultListDTO().setWeaTableColumns(weaTableColumns).setData(new PageInfo<>());
        }
        // 薪资核算结果
        List<SalaryAcctResultPO> salaryAcctResultPOS;
        // 线下导入结果
        List<ExcelAcctResultPO> excelAcctResultPOS;
        // 如果薪资核算人员太多，利用薪资核算人员id查询薪资核算结果的效率就不太好，改为直接用薪资核算记录id查询
        if (salaryAcctEmployeePOS.size() > 1000) {
            // 查询薪资核算结果
            salaryAcctResultPOS = getSalaryAcctResultService(user).listBySalaryAcctRecordIds(Collections.singleton(queryParam.getSalaryAcctRecordId()));
            // 查询线下导入结果
            excelAcctResultPOS = listBySalaryAcctRecordId(queryParam.getSalaryAcctRecordId());
        } else {
            // 薪资核算人员id
            List<Long> salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList());
            // 查询薪资核算结果
            salaryAcctResultPOS = getSalaryAcctResultService(user).listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
            // 查询线下导入结果
            excelAcctResultPOS = listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
        }

        // 查询个税扣缴义务人
        Set<Long> taxAgentIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getTaxAgentId);
        List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listByIds(taxAgentIds);
        // 查询人员信息
        List<Long> employeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getEmployeeId, Collectors.toList());
        List<DataCollectionEmployee> simpleEmployees = getSalaryEmployeeService(user).getEmployeeByIdsAll(employeeIds);
        // 判断是否存在合并计税
        Set<Long> salaryAcctEmployeeIds4ConsolidatedTax;
        if (StringUtils.isEmpty(queryParam.getConsolidatedTaxation())) {
            salaryAcctEmployeeIds4ConsolidatedTax = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId);
        } else {
            SalaryAcctEmployeeQueryParam accEmployeeQueryParam = SalaryAcctEmployeeQueryParam.builder()
                    .salaryAcctRecordId(queryParam.getSalaryAcctRecordId())
                    .ids(SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList()))
                    .build();
            List<SalaryAcctEmployeePO> salaryAcctEmployeePOS4ConsolidatedTax = getSalaryAcctEmployeeService(user).listByParam4ConsolidatedTax(accEmployeeQueryParam);
            salaryAcctEmployeeIds4ConsolidatedTax = SalaryEntityUtil.properties(salaryAcctEmployeePOS4ConsolidatedTax, SalaryAcctEmployeePO::getId);
        }
        // 系统值和线下值不一致的薪资项目id
        Set<Long> includeSalaryItemIds = Sets.newHashSetWithExpectedSize(salaryItemPOS.size());
        // 转换成薪资核算线下对比结果
        List<Map<String, Object>> resultMapList = SalaryAcctResultBO.buildComparisonTableData(salaryItemPOS, salarySobEmpFieldPOS, simpleEmployees,
                salaryAcctEmployeePOS, salaryAcctResultPOS, excelAcctResultPOS, taxAgentPOS, customParameters, salaryAcctEmployeeIds4ConsolidatedTax, includeSalaryItemIds, queryParam.isDynamicEmpInfo());
        // 系统值和线下值一致的人员
        if (queryParam.isOnlyDiffEmployee()) {
            // 过滤系统值和线下值一致的薪资核算人员
            resultMapList = resultMapList.stream()
                    .filter(map -> BooleanUtils.toBoolean(String.valueOf(map.get("different"))))
                    .collect(Collectors.toList());
        }
        // 分页
        PageInfo<Map<String, Object>> dtoPage = new PageInfo<>();
        dtoPage.setTotal(resultMapList.size());
        if (needPage) {
            dtoPage.setList(SalaryPageUtil.subList(queryParam.getCurrent(), queryParam.getPageSize(), resultMapList));
            dtoPage.setPageSize(queryParam.getPageSize());
            dtoPage.setPageNum(queryParam.getCurrent());
        } else {
            dtoPage.setList(resultMapList);
        }
        Set<Long> excludeSalaryItemIds = Sets.newHashSet();
        if (queryParam.isOnlyDiffSalaryItem()) {
            // 过滤系统值和线下值一致的薪资项目
            excludeSalaryItemIds = salaryItemPOS.stream()
                    .filter(salaryItemPO -> !includeSalaryItemIds.contains(salaryItemPO.getId()))
                    .map(SalaryItemPO::getId)
                    .collect(Collectors.toSet());
        }
        // 构建薪资核算结果列表表头
        List<WeaTableColumnGroup> weaTableColumns = SalaryAcctResultBO.buildTableColumns4ComparisonResultByGroup(salarySobItemAggregateDTO, excludeSalaryItemIds);
        // 返回结果
        return new SalaryComparisonResultListDTO().setWeaTableColumns(weaTableColumns).setData(dtoPage);
    }

    @Override
    public void batchSave(Collection<ExcelAcctResultPO> excelAcctResultPOS) {
        if (CollectionUtils.isEmpty(excelAcctResultPOS)) {
            return;
        }
        excelAcctResultPOS = encryptUtil.encryptList(new ArrayList<>(excelAcctResultPOS), ExcelAcctResultPO.class);
        List<List<ExcelAcctResultPO>> partition = Lists.partition((List) excelAcctResultPOS, 100);
        partition.forEach(getExcelAcctResultMapper()::batchInsert);
    }

    @Override
    public void deleteBySalaryAcctEmployeeIds(Collection<Long> salaryAcctEmployeeIds) {
        getExcelAcctResultMapper().deleteBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
    }

    @Override
    public void deleteBySalaryAcctRecordIds(Collection<Long> salaryAcctRecordIds) {
        getExcelAcctResultMapper().deleteBySalaryAcctRecordIds(salaryAcctRecordIds);
    }
}
